# 配置 Django 环境
import os
import sys

# reset root
root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.chdir(root_dir)
sys.path.append(root_dir)

# setup
from django.utils import timezone
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "aiops_backend.settings")
django.setup()


from django.db import transaction
from api.models import Run, Record
from scripts.auto_docker import DockerAutomation

# from aiops_backend.scripts.autoDocker import DockerAutomation
import threading
import concurrent.futures
import json, time

# error: 所指定的算法不存在，请求数据失败，打开 result.json 文件失败
my_group = "group04"
my_tag = "test"

def onlyOneMonitor(cls):
    instances = {}
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@onlyOneMonitor
class MonitorThread(threading.Thread):
    def __init__(self, max_threading_count, max_docker_count, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # print("__init__")
        self.max_threading_count = max_threading_count
        self.max_docker_count = max_docker_count
        self.cur_docker_count = Run.objects.filter(status=1).count()
        if self.cur_docker_count > self.max_docker_count:
            self.cur_docker_count = self.max_docker_count
        self._stop_event = threading.Event()
        self.executor = concurrent.futures.ThreadPoolExecutor(
            max_workers=self.max_threading_count
        )

    def stop(self):
        self._stop_event.set()
        self.executor.shutdown(wait=False)

    def run(self):
        print("begin run...")
        t = 1
        while not self._stop_event.is_set():
            # 获取未完成的任务列表
            print(f"第 {t} 次扫描...".center(100, "*"))
            t += 1
            tasks = Run.objects.all()

            futures = []
            for task in tasks:
                my_docker = DockerAutomation()
                print(f"cur_task_id: {task.id}, cur_task_status: {task.status}", end="")
                if task.status == 0:
                    print("")
                else:
                    print(f", cur_docker_id: {task.docker_id}")
                if task.status == 0:
                    # 修改状态并运行队列中的算法
                    if self.cur_docker_count < self.max_docker_count:
                        future = self.executor.submit(
                            self.run_algorithms, task, my_docker
                        )
                        future.meta = {"func_name": "run_algorithms", "task": task}
                        futures.append(future)
                        self.cur_docker_count += 1
                if task.status == 1:
                    # 检测算法是否完成
                    future = self.executor.submit(self.check_done, task, my_docker)
                    future.meta = {"func_name": "check_done", "task": task}
                    futures.append(future)
                if task.status in [2, 3, 4]:
                    # 删除算法run记录以及容器
                    future = self.executor.submit(
                        self.delete_algorithms, task, my_docker
                    )
                    future.meta = {"func_name": "delete_algorithms", "task": task}
                    futures.append(future)
            print(f"cur_docker_num: {self.cur_docker_count}")
            print(f"futures_size: {len(futures)}")

            for future in futures:
                try:
                    future.result()
                    if future.meta["func_name"] == "delete_algorithms":
                        self.cur_docker_count -= 1
                except Exception as e:
                    task = future.meta["task"]
                    if future.meta["func_name"] == "run_algorithms":
                        Run.objects.filter(id=task.id).delete()
                        self.cur_docker_count -= 1
                    else:
                        task.status = 4
                        task.save(update_fields=["status"])
                    Record.objects.filter(id=task.id).update(status=4)
            time.sleep(1)

    def run_algorithms(self, task, my_docker):
        params = json.loads(task.params)
        image_name = params["algorithm"]["name"]

        my_docker.create_run_container(
            f"{my_group}/{image_name}:{my_tag}",
            int(params["algorithm"]["start_time"]),
            int(params["algorithm"]["end_time"]),
        )
        with transaction.atomic():
            # 更新 Run表 记录
            task.status = 1
            task.docker_id = my_docker.docker_id
            task.save(update_fields=["status", "docker_id"])
            # 更新 Record表 记录
            Record.objects.filter(id=task.id).update(status=1)

    def check_done(self, task, my_docker):
        my_docker.get_container(task.docker_id)
        exit_code, output = my_docker.container.exec_run(
            cmd=["cat", "result/result.json"]
        )
        if exit_code == 0:
            result_json = json.loads(output.decode("utf-8"))
            # 运行完成
            if result_json["status"] in ["success", "error"]:
                # 更新 Record 表中的状态和信息
                with transaction.atomic():
                    next_status = 2
                    if result_json["status"] == "error":
                        next_status = 4
                    task.status = next_status
                    task.save(update_fields=["status"])
                    Record.objects.filter(id=task.id).update(
                        status=next_status,
                        result=json.dumps(result_json["result"]),
                    )
            # 运行未完成，但容器处于关闭状态（比如因服务器崩溃而停止的容器）
            elif my_docker.container.status == "exited":
                my_docker.container.start()
        else:
            raise Exception("open file error")

    def delete_algorithms(self, task, my_docker):
        # 判断是否运行
        if task.docker_id != "":
            my_docker.get_container(task.docker_id)
            my_docker.remove_docker()
        # 删除 Run 中数据
        task.delete()
